// 作业代码文件
// MyPromise
/**
 * promise准备
 * 1、promise 有三个状态 
 *      pending 等待    
 *      fulfilled  成功    
 *      rejected 失败
 * 2、resolve 和 reject 方法是用来更改状态的   有相对应的错误处理
 *      resovle: pending => fufilled 用来更改成功状态
 *      reject: pending => rejected  用来更改失败状态
 *          等待状态下阻止程序执行（控制状态不可逆）
 *          多个回调的处理
 *          错误处理
 * 3、then 方法 
 *      成功或失败之后都会调用一个回调函数并返回一个promise对象   
 *      可多次调用   
 *      有错误处理  
 *      针对promise对象和普通值的不同处理（可选参数）
 * 4、finally方法  
 *      无论成功或失败都要进行回调
 * 5、catch方法
 *      单独的失败回调
 * 6、all静态方法 
 *       启动多个异步任务，等待所有任务完成之后回调， 只要有一个任务未完成，all就未完成
 * 7、resolve/reject 静态方法
 *       resolve 返回一个带定值的promise对象
 *       reject 返回一个带失败原因的promise对象
 * 8、race 静态方法
 *      返回一个promise对象   迭代数组中的promise谁先完成   其状态是什么race返回的promise状态就是什么
 * 9、any 静态方法 
 *      接收一个Promise可迭代对象，只要其中的一个 promise 成功，就返回那个已经成功的 promise 
 */

//  定义状态的常量  便于复用
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 构造MyPromise
class MyPromise {
    constructor (executor) {
        // 捕获执行器错误
        try {
            // 执行返回
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    // promise 状态  默认等待
    status = PENDING
    // 成功之后的值 默认undefined
    value = undefined
    // 失败之后的原因 默认undefined
    reason = undefined
    // 成功回调
    sucessCallback = []
    // 失败回调
    failCallback = []
    // 更改成功状态的回调方法
    resolve = value => {
        // 如果状态不是等待 阻止程序向下执行 控制状态更改不可逆
        if(this.status !== PENDING) return
        // 状态改为成功
        this.status = FULFILLED
        // 保存成功之后的值
        this.value = value
        // 多次调用处理  判断成功回调是否存在 如果存在逐个调用
        while(this.sucessCallback.length)  this.sucessCallback.shift()()
    }
    // 更改失败状态的回调方法
    reject = reason => {
        // 如果状态不是等待 阻止程序向下执行 控制状态更改不可逆
        if(this.status !== PENDING) return
        // 状态改为成功
        this.status = REJECTED
        // 保存失败之后的原因
        this.reason = reason
        // 多次调用处理  判断失败回调是否存在 如果存在逐个调用
        while(this.failCallback.length) this.failCallback.shift()()
    }
    // 成功或失败之后都会调用一个回调函数并返回一个promise对象   可重复调用
    then (sucessCallback, failCallback) {
        // 成功/失败回调 如果不存在 用 x => x覆盖
        sucessCallback = sucessCallback ? sucessCallback : value => value
        failCallback = failCallback ? failCallback : reason => { throw reason}
        // 定义then的返回promise对象
        let promiseR = new MyPromise((resolve, reject) => {
          // 状态判断
          if(this.status === FULFILLED) {
            // 成功状态执行代码
            // 转异步获取promiseR
            setTimeout(() => {
                // 成功状态下  then的错误捕获处理
                try {
                    let x = sucessCallback(this.value)
                    // 判断x的值是否是普通值
                    // 如果是普通值  直接调用 resolve
                    // 如果是promise对象 查看promise对象返回的结果
                    // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                    resolvePromise(promiseR, x, resolve, reject)
                } catch (error) {
                    reject(error);
                }
            }, 0)
    
          } else if (this.status === REJECTED) {
            // 失败状态执行代码
            // 转异步获取promiseR
            setTimeout(() => {
                // 失败状态下  then的错误捕获处理
                try {
                    let x = failCallback(this.reason)
                    // 判断x的值是否是普通值
                    // 如果是普通值  直接调用 resolve
                    // 如果是promise对象 查看promise对象返回的结果
                    // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                    resolvePromise(promiseR, x, resolve, reject)
                } catch (error) {
                    reject(error);
                }
            }, 0)
          } else {
            // 处理异步情况  状态是等待
            // 将成功回调和失败回调存储起来
            this.sucessCallback.push(() => {
                // 成功状态执行代码
                // 转异步获取promiseR
                setTimeout(() => {
                    // 成功状态下  then的错误捕获处理
                    try {
                        let x = sucessCallback(this.value)
                        // 判断x的值是否是普通值
                        // 如果是普通值  直接调用 resolve
                        // 如果是promise对象 查看promise对象返回的结果
                        // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                        resolvePromise(promiseR, x, resolve, reject)
                    } catch (error) {
                        reject(error);
                    }
                }, 0)
            })
            this.failCallback.push(() => {
                // 失败状态执行代码
                // 转异步获取promiseR
                setTimeout(() => {
                    // 失败状态下  then的错误捕获处理
                    try {
                        let x = failCallback(this.reason)
                        // 判断x的值是否是普通值
                        // 如果是普通值  直接调用 resolve
                        // 如果是promise对象 查看promise对象返回的结果
                        // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                        resolvePromise(promiseR, x, resolve, reject)
                    } catch (error) {
                        reject(error);
                    }
                }, 0)
            })
          }
        })
        return promiseR
    }
    // 无论成功或失败都要回调
    finally (callback) {
        return this.then(value => {
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }
    // 失败单独回调
    catch (failCallback) {
        // 回调then方法  将成功回调置为undefined
        return this.then(undefined, failCallback)
    }
    // all是个静态方法 启动多个异步任务，等待所有任务完成之后回调， 只要有一个任务未完成，all就未完成
    static all (array) {
        let reslut = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            // addData方法用来计数判断是否所有任务是否已完成，若完成则执行成功回调
            function addData (key, value) {
                reslut[key] = value
                index ++
                if(index === array.length) {
                    resolve(reslut)
                }
            }
            // 循环执行所有任务 并判断任务是否为promise对象  是则通过then执行回调，成功则计数，失败抛出错误；若为普通值  则直接计数
            for (let i = 0; i < array.length; i ++) {
                let current = array[i]
                if (current instanceof MyPromise) {
                    //  promise 对象
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值
                    addData(i, array[i])
                }
            }
        })
    }
    // resolve 返回一个带定值的promise对象
    static resolve (value) {
        if (value instanceof MyPromise) return value
        return new MyPromise(resolve => resolve(value))
    }
    // reject 返回一个带失败原因的promise对象
    static reject (reason) {
        if (reason instanceof MyPromise) {
            return reason.then(this.reject)
        } else {
            return new MyPromise((undefined, reject) => reject(reason))
        }
    }
    // 返回一个promise对象   迭代数组中的promise谁先完成   其状态是什么race返回的promise状态就是什么
    static race (array) {
        return new MyPromise((resolve, reject) => {
            // 循环执行所有任务 并判断任务是否为promise对象  是则通过then直接执行回调；若为普通值  则直接执行成功回调
            for (let i = 0; i < array.length; i ++) {
                let current = array[i]
                if (current instanceof MyPromise) {
                    //  promise 对象
                    current.then(resolve, reject)
                } else {
                    // 普通值
                    resolve(current)
                }
            }
        })
    }
    // 接收一个Promise可迭代对象，只要其中的一个 promise 成功，就返回那个已经成功的 promise 
    static any(array) {
        let isSuccess = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            // 所有任务执行状态储存， 并计数任务执行，
            // 任务执行中判断是否有已成功的状态  如有返回成功的那个状态  无则继续执行
            // 当所有任务全部完成后 说明所有任务均未完成 抛出错误
            function addSuccess (key, status, value) {
                isSuccess[key] = status
                index ++
                if(status) {
                    resolve(value)
                } else if(index === array.length) {
                    reject(new Error("all fail"))
                }
            }

            // 循环执行所有任务 并判断任务是否为promise对象  是则通过then执行回调，并执行addSuccess 成功传入true 失败传入false；若为普通值  addSuccess
            for (let i = 0; i < array.length; i ++) {
                let current = array[i]
                if (current instanceof MyPromise) {
                    //  promise 对象
                    current.then(value => addSuccess(i, true, value), reason => addSuccess(i, false, reason))
                } else {
                    // 普通值
                    addSuccess(i, true, current)
                }
            }
        })
    }


}

function resolvePromise(promiseR, x, resolve, reject) {
    if (promiseR === x) {
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
      // promise 对象
        x.then(resolve, reject)
    } else {
      // 普通值
        resolve(x)
    }
  }

  module.exports = MyPromise